package vip.liux.contracts.shared.kits;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.util.UriComponentsBuilder;
import vip.liux.contracts.shared.properties.FilesProperties;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/**
 * StorageKit 类负责文件的存储、删除和读取操作。
 * 使用一致性哈希算法将文件分布到不同的文件夹中。
 */
@Component
public class StorageKit implements BeanFactoryAware {

    // 使用 Guava 提供的 murmur3_32 哈希函数
    public static final HashFunction hash = Hashing.murmur3_32_fixed();
    // 定义文件夹列表，用于存储文件
    private static final List<String> HASH_FOLDERS = List.of("00001", "00002", "00003", "00004", "00005", "00006", "00007", "00008", "00009", "00010", "00011", "00012");
    // 文件存储配置属性
    private static FilesProperties filesProperties;
    // Spring Bean 工厂，用于获取 FilesProperties 实例
    private BeanFactory beanFactory;

    /**
     * 保存文件到对应的哈希文件夹中。
     *
     * @param key   文件的唯一标识
     * @param bytes 文件内容的字节数组
     */
    @SneakyThrows
    public static void save(String key, byte[] bytes) {
        // 根据 key 计算文件路径
        Path filePath = getFilePath(key);
        // 如果文件夹不存在，则创建文件夹
        if (!Files.exists(filePath.getParent())) {
            Files.createDirectories(filePath.getParent());
        }
        // 将文件内容写入到目标路径
        FileCopyUtils.copy(bytes, filePath.toFile());
    }

    /**
     * 删除文件。
     *
     * @param key 文件的唯一标识
     */
    @SneakyThrows
    public static void delete(String key) {
        // 根据 key 计算文件路径
        Path filePath = getFilePath(key);
        // 删除文件或文件夹
        FileSystemUtils.deleteRecursively(filePath);
    }

    /**
     * 获取文件内容。
     *
     * @param key 文件的唯一标识
     * @return 文件内容的字节数组
     */
    @SneakyThrows
    public static byte[] getContent(String key) {
        // 根据 key 计算文件路径
        Path filePath = getFilePath(key);
        // 检查文件是否存在
        if (!filePath.toFile().exists()) {
            throw new IOException("文件未找到: " + filePath);
        }
        // 读取文件内容并返回
        return FileCopyUtils.copyToByteArray(filePath.toFile());
    }

    /**
     * 获取文件的输入流。
     *
     * @param key 文件的唯一标识
     * @return 文件内容的输入流
     */
    @SneakyThrows
    public static InputStream getInputStream(String key) {
        // 根据 key 计算文件路径
        Path filePath = getFilePath(key);
        // 检查文件是否存在
        if (!filePath.toFile().exists()) {
            throw new IOException("文件未找到: " + filePath);
        }
        // 返回文件内容的输入流
        return new ByteArrayInputStream(FileCopyUtils.copyToByteArray(filePath.toFile()));
    }

    /**
     * 根据 key 计算文件的完整路径。
     * 使用一致性哈希算法将文件分布到不同的文件夹中。
     *
     * @param key 文件的唯一标识
     * @return 文件的完整路径
     */
    private static Path getFilePath(String key) {
        // 返回完整路径
        return Path.of(filesProperties.getLocalPath(), getPath(key));
    }

    private static String getPath(String key) {
        // 计算 key 对应的文件夹索引
        int folderIndex = Hashing.consistentHash(hash.hashUnencodedChars(key), HASH_FOLDERS.size());
        // 获取对应的文件夹名称
        return HASH_FOLDERS.get(folderIndex) + "/" + key;
    }

    private static String getDomain() {
        // 返回文件存储的域名
        return filesProperties.getDomain();
    }

    public static String getCdnUrl(String key) {
        // 返回文件的完整 URL
        return UriComponentsBuilder.fromHttpUrl(getDomain()).path(getPath(key)).build().toUriString();
    }

    /**
     * 初始化方法，在组件创建后调用。
     * 从 Spring Bean 工厂中获取 FilesProperties 实例。
     */
    @PostConstruct
    private void init() {
        filesProperties = beanFactory.getBean(FilesProperties.class);
    }

    /**
     * 设置 Bean 工厂。
     *
     * @param beanFactory Spring Bean 工厂
     * @throws BeansException 如果设置失败
     */
    @Override
    public void setBeanFactory(@NotNull BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}